1 =============================================================================
2 APPLICATION : CSUACSelfElevation Project Overview
3 =============================================================================
5 /////////////////////////////////////////////////////////////////////////////
8 User Account Control (UAC) is a new security component in Windows Vista and
9 newer operating systems. With UAC fully enabled, interactive administrators
10 normally run with least user privileges. This example demonstrates how to
11 check the privilege level of the current process, and how to self-elevate
12 the process by giving explicit consent with the Consent UI.
15 /////////////////////////////////////////////////////////////////////////////
18 You must run this sample on Windows Vista or newer operating systems.
21 /////////////////////////////////////////////////////////////////////////////
24 The following steps walk through a demonstration of the UAC sample.
26 Step1. After you successfully build the sample project in Visual Studio 2008,
27 you will get an application: CSUACSelfElevation.exe.
29 Step2. Run the application as a protected administrator on a Windows Vista or
30 Windows 7 system with UAC fully enabled. The application should display the
31 following content on the main dialog.
33 IsUserInAdminGroup: True
35 IsProcessElevated: False
36 Integrity Level: Medium
38 There is a UAC shield icon on the Self-elevate button.
40 Step3. Click on the Self-elevate button. You will see a Consent UI.
43 ----------------------------------
44 Do you want to allow the following program from an unknown publisher to
45 make changes to this computer?
47 Step4. Click Yes to approve the elevation. The original application will then
48 be started and display the following content on the main dialog.
50 IsUserInAdminGroup: True
52 IsProcessElevated: True
55 The Self-elevate button on the dialog does not have the UAC shield icon this
56 time. That is, the application is running as elevated administrator. The
57 elevation succeeds. If you click on the Self-elevate button again, the
58 application will tell you that it is running as administrator.
60 Step5. Close the application.
63 /////////////////////////////////////////////////////////////////////////////
66 Step1. Create a new Visual C# Windows Forms project named CSUACSelfElevation.
68 Step2. Add controls to the main form
72 Caption: "Self-elevate"
76 Use: Display whether the primary access token of the process belongs to
77 user account that is a member of the local Administrators group, even if it
78 currently is not elevated.
82 Use: Display whether the application is run as administrator.
86 Use: Display whether the process is elevated or not. Token elevation is
87 only available on Windows Vista and newer operating systems. The label
88 shows N/A on systems prior to Windows Vista.
92 Use: Display the integrity level of the current process. Integrity level is
93 only available on Windows Vista and newer operating systems. The label
94 shows N/A on systems prior to Windows Vista.
96 Step3. Check and display the current process's "run as administrator" status,
97 elevation information and integrity level when the application initializes
100 Create the following four helper functions:
103 /// The function checks whether the primary access token of the process belongs
104 /// to user account that is a member of the local Administrators group, even if
105 /// it currently is not elevated.
108 /// Returns true if the primary access token of the process belongs to user
109 /// account that is a member of the local Administrators group. Returns false
110 /// if the token does not.
112 /// <exception cref="System.ComponentModel.Win32Exception">
113 /// When any native Windows API call fails, the function throws a Win32Exception
114 /// with the last error code.
116 internal bool IsUserInAdminGroup()
119 /// The function checks whether the current process is run as administrator.
120 /// In other words, it dictates whether the primary access token of the
121 /// process belongs to user account that is a member of the local
122 /// Administrators group and it is elevated.
125 /// Returns true if the primary access token of the process belongs to user
126 /// account that is a member of the local Administrators group and it is
127 /// elevated. Returns false if the token does not.
129 internal bool IsRunAsAdmin()
132 /// The function gets the elevation information of the current process. It
133 /// dictates whether the process is elevated or not. Token elevation is only
134 /// available on Windows Vista and newer operating systems, thus
135 /// IsProcessElevated throws a C++ exception if it is called on systems prior
136 /// to Windows Vista. It is not appropriate to use this function to determine
137 /// whether a process is run as administartor.
140 /// Returns true if the process is elevated. Returns false if it is not.
142 /// <exception cref="System.ComponentModel.Win32Exception">
143 /// When any native Windows API call fails, the function throws a Win32Exception
144 /// with the last error code.
147 /// TOKEN_INFORMATION_CLASS provides TokenElevationType to check the elevation
148 /// type (TokenElevationTypeDefault / TokenElevationTypeLimited /
149 /// TokenElevationTypeFull) of the process. It is different from TokenElevation
150 /// in that, when UAC is turned off, elevation type always returns
151 /// TokenElevationTypeDefault even though the process is elevated (Integrity
152 /// Level == High). In other words, it is not safe to say if the process is
153 /// elevated based on elevation type. Instead, we should use TokenElevation.
155 internal bool IsProcessElevated()
158 /// The function gets the integrity level of the current process. Integrity
159 /// level is only available on Windows Vista and newer operating systems, thus
160 /// GetProcessIntegrityLevel throws a C++ exception if it is called on systems
161 /// prior to Windows Vista.
164 /// Returns the integrity level of the current process. It is usually one of
167 /// SECURITY_MANDATORY_UNTRUSTED_RID - means untrusted level. It is used
168 /// by processes started by the Anonymous group. Blocks most write access.
169 /// (SID: S-1-16-0x0)
171 /// SECURITY_MANDATORY_LOW_RID - means low integrity level. It is used by
172 /// Protected Mode Internet Explorer. Blocks write acess to most objects
173 /// (such as files and registry keys) on the system. (SID: S-1-16-0x1000)
175 /// SECURITY_MANDATORY_MEDIUM_RID - means medium integrity level. It is
176 /// used by normal applications being launched while UAC is enabled.
177 /// (SID: S-1-16-0x2000)
179 /// SECURITY_MANDATORY_HIGH_RID - means high integrity level. It is used
180 /// by administrative applications launched through elevation when UAC is
181 /// enabled, or normal applications if UAC is disabled and the user is an
182 /// administrator. (SID: S-1-16-0x3000)
184 /// SECURITY_MANDATORY_SYSTEM_RID - means system integrity level. It is
185 /// used by services and other system-level applications (such as Wininit,
186 /// Winlogon, Smss, etc.) (SID: S-1-16-0x4000)
189 /// <exception cref="System.ComponentModel.Win32Exception">
190 /// When any native Windows API call fails, the function throws a Win32Exception
191 /// with the last error code.
193 internal int GetProcessIntegrityLevel()
195 Some of the methods need to P/Invoke some native Windows APIs. The P/Invoke
196 signatures are defined in NativeMethod.cs.
198 In the constructor of the main form, check and display the "run as
199 administrator" status, the elevation information, and the integrity level of
202 // Get and display whether the primary access token of the process belongs
203 // to user account that is a member of the local Administrators group even
204 // if it currently is not elevated (IsUserInAdminGroup).
207 bool fInAdminGroup = IsUserInAdminGroup();
208 this.lbInAdminGroup.Text = fInAdminGroup.ToString();
212 this.lbInAdminGroup.Text = "N/A";
213 MessageBox.Show(ex.Message, "An error occurred in IsUserInAdminGroup",
214 MessageBoxButtons.OK, MessageBoxIcon.Error);
217 // Get and display whether the process is run as administrator or not
221 bool fIsRunAsAdmin = IsRunAsAdmin();
222 this.lbIsRunAsAdmin.Text = fIsRunAsAdmin.ToString();
226 this.lbIsRunAsAdmin.Text = "N/A";
227 MessageBox.Show(ex.Message, "An error occurred in IsRunAsAdmin",
228 MessageBoxButtons.OK, MessageBoxIcon.Error);
232 // Get and display the process elevation information (IsProcessElevated)
233 // and integrity level (GetProcessIntegrityLevel). The information is not
234 // available on operating systems prior to Windows Vista.
235 if (Environment.OSVersion.Version.Major >= 6)
237 // Running Windows Vista or later (major version >= 6).
241 // Get and display the process elevation information.
242 bool fIsElevated = IsProcessElevated();
243 this.lbIsElevated.Text = fIsElevated.ToString();
245 // Update the Self-elevate button to show the UAC shield icon on
246 // the UI if the process is not elevated.
247 this.btnElevate.FlatStyle = FlatStyle.System;
248 NativeMethod.SendMessage(btnElevate.Handle,
249 NativeMethod.BCM_SETSHIELD, 0,
250 fIsElevated ? IntPtr.Zero : (IntPtr)1);
254 this.lbIsElevated.Text = "N/A";
255 MessageBox.Show(ex.Message, "An error occurred in IsProcessElevated",
256 MessageBoxButtons.OK, MessageBoxIcon.Error);
261 // Get and display the process integrity level.
262 int IL = GetProcessIntegrityLevel();
265 case NativeMethod.SECURITY_MANDATORY_UNTRUSTED_RID:
266 this.lbIntegrityLevel.Text = "Untrusted"; break;
267 case NativeMethod.SECURITY_MANDATORY_LOW_RID:
268 this.lbIntegrityLevel.Text = "Low"; break;
269 case NativeMethod.SECURITY_MANDATORY_MEDIUM_RID:
270 this.lbIntegrityLevel.Text = "Medium"; break;
271 case NativeMethod.SECURITY_MANDATORY_HIGH_RID:
272 this.lbIntegrityLevel.Text = "High"; break;
273 case NativeMethod.SECURITY_MANDATORY_SYSTEM_RID:
274 this.lbIntegrityLevel.Text = "System"; break;
276 this.lbIntegrityLevel.Text = "Unknown"; break;
281 this.lbIntegrityLevel.Text = "N/A";
282 MessageBox.Show(ex.Message, "An error occurred in GetProcessIntegrityLevel",
283 MessageBoxButtons.OK, MessageBoxIcon.Error);
288 this.lbIsElevated.Text = "N/A";
289 this.lbIntegrityLevel.Text = "N/A";
292 Step4. Handle the click event of the Self-elevate button. When user clicks
293 the button, elevate the process by restarting itself with
294 ProcessStartInfo.UseShellExecute = true and ProcessStartInfo.Verb = "runas"
295 if the process is not run as administrator.
297 private void btnElevate_Click(object sender, EventArgs e)
299 // Elevate the process if it is not run as administrator.
302 // Launch itself as administrator
303 ProcessStartInfo proc = new ProcessStartInfo();
304 proc.UseShellExecute = true;
305 proc.WorkingDirectory = Environment.CurrentDirectory;
306 proc.FileName = Application.ExecutablePath;
315 // The user refused the elevation.
316 // Do nothing and return directly ...
320 Application.Exit(); // Quit itself
324 MessageBox.Show("The process is running as administrator", "UAC");
328 Step5. Automatically elevate the process when it's started up.
330 If your application always requires administrative privileges, such as during
331 an installation step, the operating system can automatically prompt the user
332 for privileges elevation each time your application is invoked.
334 If a specific kind of resource (RT_MANIFEST) is found embedded within the
335 application executable, the system looks for the <trustInfo> section and
336 parses its contents. Here is an example of this section in the manifest file:
338 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
340 <requestedPrivileges>
341 <requestedExecutionLevel
342 level="requireAdministrator"
344 </requestedPrivileges>
348 Three different values are possible for the level attribute
350 a) requireAdministrator
351 The application must be started with Administrator privileges; it won't run
355 The application is started with the highest possible privileges.
356 If the user is logged on with an Administrator account, an elevation prompt
357 appears. If the user is a Standard User, the application is started
358 (without any elevation prompt) with these standard privileges.
361 The application is started with the same privileges as the calling
364 To configure the elevation level in this Visual C# Windows Forms project,
365 open the project's properties, turn to the Security tab, check the checkbox
366 "Enable ClickOnce Security Settings", check "This is a fulltrust application"
367 and close the application Properies page. This creates an app.manifest file
368 and configures the project to embed the manifest. You can open the
369 "app.manifest" file from Solution Explorer by expanding the Properies folder.
370 The file has the following content by default.
372 <?xml version="1.0" encoding="utf-8"?>
373 <asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"
374 xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
375 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
376 <assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
377 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
379 <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
380 <!-- UAC Manifest Options
381 If you want to change the Windows User Account Control level replace the
382 requestedExecutionLevel node with one of the following.
384 <requestedExecutionLevel level="asInvoker" uiAccess="false" />
385 <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
386 <requestedExecutionLevel level="highestAvailable" uiAccess="false" />
388 If you want to utilize File and Registry Virtualization for backward
389 compatibility then delete the requestedExecutionLevel node.
391 <requestedExecutionLevel level="asInvoker" uiAccess="false" />
392 </requestedPrivileges>
393 <applicationRequestMinimum>
394 <PermissionSet class="System.Security.PermissionSet" version="1"
395 Unrestricted="true" ID="Custom" SameSite="site" />
396 <defaultAssemblyRequest permissionSetReference="Custom" />
397 </applicationRequestMinimum>
402 Here we are focusing on the line:
404 <requestedExecutionLevel level="asInvoker" uiAccess="false" />
406 You can change it to be
408 <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
410 to require the application always be started with Administrator privileges.
413 /////////////////////////////////////////////////////////////////////////////
416 MSDN: User Account Control
417 http://msdn.microsoft.com/en-us/library/aa511445.aspx
419 MSDN: Windows Vista Application Development Requirements for User Account
420 Control Compatibility
421 http://msdn.microsoft.com/en-us/library/bb530410.aspx
424 /////////////////////////////////////////////////////////////////////////////